<?php
/*
 * @Author: your name
 * @Date: 2020-10-14 16:44:57
 * @LastEditTime: 2020-12-28 14:59:57
 * @LastEditors: Please set LastEditors
 * @Description: 防火墙设置
 * @FilePath: /special/application/adminapiv3/service/module_manage/netfilter/FirewallSetService.php
 */
namespace app\adminapiv3\service\module_manage\netfilter;

use app\adminapiv3\model\NetfilterFirewallRuleModel;
use app\adminapiv3\service\BaseService;
use app\adminapiv3\service\module_manage\CheckHostService;
use app\common\exception\CustomException;
use app\common\definition\netfilter\FirewallRuleDefinition;
use app\common\definition\netfilter\NetfilterConfigDefinition;
use think\Db;
use think\Exception;

class FirewallSetService extends BaseService
{
    /**
     * @name: 防火墙规则配置
     * @Param: 
     * @Description: 
     * @Author: foo
     * @Date: 2019-10-31 14:54:25
     */
    public function linuxFirewallConfigModify ($input)
    {
        //添加操作日志
        $firewall_a = Db::name('netfilter_firewall_config_linux')->where('host_id', $input['host_id'])->find();
        
        $host_data = $this->getHost('name');
        $host_name = implode(',', $host_data);
        $host_id_a = array_keys($host_data);
        
        //检测主机是否在线
        $CheckHostService = new CheckHostService();
        $CheckHostService->online($host_id_a);
        
        global $logArray;
        $logArray = array(
            'content' => '修改防火墙默认处理行为',
            'new_info' => '修改为:' . $this->linuxOptionLog($input),
            'old_info' => '主机:' . $host_name . $this->linuxOptionLog($firewall_a)
        );

        $old_set_rule = Db::name('netfilter_firewall_rule')->where('host_id', $input['host_id'])->where('source', FirewallRuleDefinition::FIREWALL_SET_RULE_SOURCE)->select();
        $new_set_rule = $this->getLinuxFirewallSetRule($input);

        $firewall_rule = $this->getFirewallSetRuleData($old_set_rule, $new_set_rule, $input['host_id']);
        Db::startTrans();
        try{
            $NetfilterFirewallRuleModel = new NetfilterFirewallRuleModel();
            $NetfilterFirewallRuleModel->saveAll($firewall_rule);
            Db::name('netfilter_firewall_config_linux')->where('host_id', $input['host_id'])->update($input);
            Db::commit();
            $SortRuleService = new SortRuleService();
            $SortRuleService->pushContentToFile($input['host_id']);
        } catch (Exception $e)
        {
            Db::rollback();
            throw new CustomException('更改防火墙设置失败:'.$e->getMessage(), 500);
        }
    }

    /**
     * @name: windows 防火墙设置修改
     * @Param: 
     * @Description: 
     * @Author: foo
     * @Date: 2020-12-15 15:01:49
     */
    public function windowsFirewallConfigModify ($input)
    {
        //添加操作日志
        $firewall_a = Db::name('netfilter_firewall_config_windows')->where('host_id', $input['host_id'])->find();

        $host_data = $this->getHost('name');
        $host_name = implode(',', $host_data);
        $host_id_a = array_keys($host_data);
        //检测主机是否在线
        $CheckHostService = new CheckHostService();
        $CheckHostService->online($host_id_a);
        global $logArray;
        $logArray = array(
            'content' => '修改防火墙默认处理行为',
            'new_info' => '修改为:' . $this->windowsOptionLog($input),
            'old_info' => '主机:' . $host_name . $this->windowsOptionLog($firewall_a)
        );

        $old_set_rule = Db::name('netfilter_firewall_rule')->where('host_id', $input['host_id'])->where('source', FirewallRuleDefinition::FIREWALL_SET_RULE_SOURCE)->select();
        $new_set_rule = $this->getWindowsFirewallSetRule($input);

        $firewall_rule = $this->getFirewallSetRuleData($old_set_rule, $new_set_rule, $input['host_id']);

        Db::startTrans();
        try{
            $NetfilterFirewallRuleModel = new NetfilterFirewallRuleModel();
            $NetfilterFirewallRuleModel->saveAll($firewall_rule);
            Db::name('netfilter_firewall_config_windows')->where('host_id', $input['host_id'])->update($input);
            Db::commit();
        } catch (Exception $e)
        {
            Db::rollback();
            throw new CustomException('更改防火墙设置失败:'.$e->getMessage(), 500);
        }
    }

    /**
     * @name: 控制防火墙
     * @Param: 
     * @Description: 
     * @Author: foo
     * @Date: 2020-10-29 15:10:18
     */
    public function controlFirewall ()
    {
        $host_a = $this->getHost();
        
        $host_id_a = array_keys($host_a);
        //检测主机是否在线
        $CheckHostService = new CheckHostService();
        $CheckHostService->online($host_id_a);

        //添加用户操作日志
        global $logArray;
        $logArray = [
            'content' => implode(',', array_column($host_a, 'name')) . '接管防火墙',
        ];
        
        Db::startTrans();
        try {
            Db::name('host_learn_config')->whereIn('host_id', $host_id_a)->update(
                [
                    'controlled_firewall' => NetfilterConfigDefinition::FIREWALL_CONTROLLED
                ]
            );
            Db::commit();
        } catch (Exception $e)
        {
            Db::rollback();
            throw new CustomException('接管防火墙失败:' . $e->getMessage(), 522);
        }
    }

    /**
     * @name: 放弃控制防火墙
     * @Param: 
     * @Description: 
     * @Author: foo
     * @Date: 2020-10-29 15:10:18
     */
    public function uncontrolFirewall ($end_time)
    {
        $max_exprie_time = time() + 86500;

        $host_id_a = $this->getHost('id');
        //检测主机是否在线
        $CheckHostService = new CheckHostService();
        $CheckHostService->online($host_id_a);

        global $logArray;
        $logArray = [
            'content' => '取消接管防火墙,' . $end_time == 0 ? '永久取消接管' : '临时取消接管,取消接管时间:'. $end_time . '秒',
        ];
        
        if ($end_time != 0 && $end_time < time() - 100)
        {
            throw new CustomException('放弃接管防火墙结束时间不能早于当前时间', 422);
        }

        if ($end_time > $max_exprie_time)
        {
            throw new CustomException('放弃接管防火墙结束时间不能超过24小时', 422);
        }
        
        Db::name('host_learn_config')->whereIn('host_id', $host_id_a)->update(
            [
                'controlled_firewall' => NetfilterConfigDefinition::FIREWALL_NOT_CONTROLLED,
                'uncontrol_firewall_etime' => $end_time
            ]
        );
    }

    /**
     * @name: 切割防火墙设置
     * @Param: 
     * @Description: 
     * @Author: foo
     * @Date: 2020-12-09 10:48:55
     */
    public function getLinuxFirewallSetRule (array $firewall_set)
    {
        //防火墙路由转发 默认为开启
        $return = [
            //当防火墙开关->关闭 或者 入站开关->开启 = 入站默认规则为允许 2为规则id
            1 => $firewall_set['input'] || !$firewall_set['main_switch'] ? '-P INPUT ACCEPT' : '-P INPUT DROP',
            //暂时没有防火墙转换开关命令的设置,所以设置为默认开启 2为规则id
            2 => $firewall_set['forward'] || !$firewall_set['main_switch'] ? '-P FORWARD ACCEPT' : '-P FORWARD DROP',
            //当防火墙开关->关闭 或者 出站开关->开启 = 出站默认规则为允许 3为规则id
            3 => $firewall_set['output'] || !$firewall_set['main_switch'] ? '-P OUTPUT ACCEPT': '-P OUTPUT DROP'
        ];

        return $return;
    }

    /**
     * @name: 获取windows·防火墙规则
     * @Param: 
     * @Description: 
     * @Author: foo
     * @Date: 2020-12-15 13:47:43
     */
    public function getWindowsFirewallSetRule (array $firewall_set)
    {
        $data = [];
        $this->windowFirewallSet('public', $firewall_set, $data);
        $this->windowFirewallSet('private', $firewall_set, $data);
        $this->windowFirewallSet('domain', $firewall_set, $data);

        foreach ($data as $k => $rule)
        {
            $return[$k+1] = $rule;
        }
        return $return;
    }

    /**
     * @name: windows防火墙设置
     * @Param: 
     * @Description: 
     * @Author: foo
     * @Date: 2020-12-15 15:26:16
     */
    private function windowFirewallSet (string $type, array $firewall_set, array &$return)
    {
        $output_s = $firewall_set[$type . '_output'] ? 'allow' : 'block';
        $input_s = $firewall_set[$type . '_input'] ? 'allow' : 'block';
        $main_switch_s = $firewall_set[$type . '_main_switch'] ? 'on' : 'off';
        
        $return[] = 'netsh advfirewall set ' . $type . 'profile firewallpolicy ' . $input_s . 'inbound,' . $output_s . 'outbound';
        $return[] = 'netsh advfirewall set ' . $type . 'profile state ' . $main_switch_s;
        
    }

    /**
     * @name: 获取防火墙配置规则数据
     * @Param: 
     * @Description: 
     * @Author: foo
     * @Date: 2020-12-15 15:07:43
     */
    private function getFirewallSetRuleData ($old_set_rule, $new_set_rule, $host_id)
    {
        $return = [];
        if($old_set_rule)
        {
            foreach ($old_set_rule as $k => $v)
            {
                $v['content'] = $new_set_rule[$v['rule_id']];
                $return[$k] = $v;
            }
        }
        else
        {
            foreach ($new_set_rule as $rule_id => $content)
            {
                $return[] = [
                    'rule_id' => $rule_id,
                    'host_id' => $host_id,
                    'content' => $content,
                    'source' => FirewallRuleDefinition::FIREWALL_SET_RULE_SOURCE,
                    'region' => FirewallRuleDefinition::FIREWALL_DEFAULT_CONFIG_RULE_REGION,
                    'rank' => 0
                ];
            }
        }

        return $return;

    }

    /**
     * @name: linux防火墙配置用户操作日志
     * @Param: 
     * @Description: 
     * @Author: foo
     * @Date: 2020-12-15 13:27:39
     */
    private function linuxOptionLog ($data)
    {
        $return = '防火墙主开关:' ;
        $return .= $data['main_switch'] ? '开启' : '关闭';
        $return .= '入站默认处理方式:' ;
        $return .= $data['input'] ? '允许' : '拒绝' ;
        $return .= '出站默认处理方式:' ;
        $return .= $data['output'] ? '允许' : '拒绝' ;
        $return .= '路由转发:' ;
        $return .= $data['forward'] ? '允许' : '拒绝' ;

        return $return;
    }
    /**
     * @name: 用户操作日志
     * @Param: 
     * @Description: 
     * @Author: foo
     * @Date: 2020-12-15 13:27:39
     */
    private function windowsOptionLog ($data)
    {
        $return = '私网防火墙配置:' ;
        $return .= $data['private_main_switch'] ? '开启' : '关闭';
        $return .= $data['private_input'] ? '开启' : '关闭';
        $return .= $data['private_output'] ? '开启' : '关闭';
        $return .= '公网防火墙配置:' ;
        $return .= $data['public_main_switch'] ? '允许' : '拒绝';
        $return .= $data['public_input'] ? '允许' : '拒绝';
        $return .= $data['public_output'] ? '允许' : '拒绝';
        $return .= '域防火墙配置:' ;
        $return .= $data['domain_main_switch'] ? '允许' : '拒绝';
        $return .= $data['domain_input'] ? '允许' : '拒绝';
        $return .= $data['domain_output'] ? '允许' : '拒绝';

        return $return;
    }
}